Part 1: stats
install.packages("wordspace") # contains dist.matrix function
also installing the dependencies ‘sparsesvd’, ‘iotools’
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/sparsesvd_0.2.tgz'
Content type 'application/x-gzip' length 74751 bytes (72 KB)
==================================================
downloaded 72 KB
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/iotools_0.3-2.tgz'
Content type 'application/x-gzip' length 154104 bytes (150 KB)
==================================================
downloaded 150 KB
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/wordspace_0.2-6.tgz'
Content type 'application/x-gzip' length 2509428 bytes (2.4 MB)
==================================================
downloaded 2.4 MB
The downloaded binary packages are in
/var/folders/k4/khtkczkd5tn732ftjpwgtr240000gn/T//RtmpHMlGUN/downloaded_packages
library("wordspace")
Loading required package: Matrix
Attaching package: ‘Matrix’
The following object is masked from ‘package:flowCore’:
%&%
dm <- dist.matrix(m, as.dist = TRUE)
Error: vector memory exhausted (limit reached?)
Saved the flowset object I created and load the object here - but I didn’t save the filtered df converted to matrix, which is needed for the stats below
fs <- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/9MBO/prepro_outsjan20-9000cells/FlowSOMin.rds")
# note to Shuming - we should be able to go directly from flowset in preprocessing into this flowset but then the 9 samples (or however many will be in separate frames)
# I don't know where the actual values are located
# skip
#based on flowsom, the optimal k number of clusters = 5 for the sample (n=3000)
metacl <- MetaClustering(fs$map$codes,
"metaClustering_consensus",
max = 15)
unique(metacl[fs$map$mapping[, 1]])
dm = dist.matrix(m, as.dist = TRUE)
Error: vector memory exhausted (limit reached?)
# run the function to get the stats
krange <- 3:15 #range of number of clusters
li = stats(krange) # doesn't contain silhouette
# list of 2
#silhouette score: ranges from -1 to 1
#-1: bad clusters 0: neutral, indifferent 1: good clusters
#plot(krange, type='b', li[[1]][krange], xlab='Number of clusters', ylab='Average Silhouette Scores', frame=TRUE)
#Calinski-Harabasz index:
# the highest value is the optimal number of clusters
plot(krange, type='b', li[[1]][krange], xlab='Number of clusters', ylab='Calinski-Harabasz index', frame=TRUE)
#Davies–Bouldin index: minimum score is zero
#the lowest value is the optimal number of clusters
plot(krange, type='b', li[[2]][krange], xlab='Number of clusters', ylab='Davies–Bouldin index', frame=TRUE)
The Calinski-Harabasz index show that K=8 is best number of clusters. But the Davies–Bouldin index shows lower is better. It is okay up until 9.
Try statistics on other clustering.
ch.pheno.k.50 = calinhara(m,df$phenograph,length((unique(df$phenograph))))
Error in if (cln[i] < 2) cclx <- 0 : argument is of length zero
David Bouldin index for Phenograph
#calculate Davies–Bouldin index
# indiex.list, intracls, intercls
# I'm not sure how this is working but it does run
dbi.phen.k.271 <- clv.Davies.Bouldin(cls.scatt.data(
m,
df$phenograph_clusterk271),
intracls = "average",
intercls = "average"
)
dbi.phen.k.50 <- clv.Davies.Bouldin(cls.scatt.data(
m,
df$phenograph_cluster),
intracls = "average",
intercls = "average"
)
make this calculations on the Seurat clustering
# read in the seurat object with the clustering values
seu <- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/9MBO/prepro_outsjan20-9000cells/SeuratfromFlowsomPheno.rds" )
CH index and DBI from Seurat (Louvain) try one resolution first
dbi.louv.res.1 <- clv.Davies.Bouldin(cls.scatt.data(
m,
seu@meta.data$RNA_snn_res.1),
intracls = "average",
intercls = "average"
)
Error in cls.id.vect.validity(clust, "clust") :
Bad usage: input 'clust' should be vector type.
NA
Calculate all CHI for Louvain
ch_louv <-list()
l.range <- c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
resolution = c(0.1,0.25,0.5,0.75,1)
prefix = "RNA_snn_res."
# loop doesn't work because I can't index the seurat resolutions
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.1,length((unique(seu@meta.data$RNA_snn_res.0.1))))
ch_louv["RNA_snn_res.0.1"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.25,length((unique(seu@meta.data$RNA_snn_res.0.25))))
ch_louv["RNA_snn_res.0.25"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.5,length((unique(seu@meta.data$RNA_snn_res.0.5))))
ch_louv["RNA_snn_res.0.5"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.75,length((unique(seu@meta.data$RNA_snn_res.0.75))))
ch_louv["RNA_snn_res.0.75"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.1,length((unique(seu@meta.data$RNA_snn_res.1))))
ch_louv["RNA_snn_res.1"] <- ch
Plot the CHI from Louvain clusters

Make a table with the CHI outputs
df.chi <- as.data.frame(Method, CHI.value)
Warning in as.data.frame.vector(x, ..., nm = nm) :
'row.names' is not a character vector of length 7 -- omitting it. Will be an error!
Make another plot
#library(ggplot2)
ggplot(df.chi, aes(x= Method, y= CHI.value)) + geom_point() +
theme(axis.text.x = element_text(angle = 90))

Part 2: plot
#transpose the csv so that seurat object has the right column and row
# the flow intensity values will be input as RNA expression
tm <- t(df2)
rownames(tm) <- colnames(df2)
colnames(tm) <- rownames(df2)
k <- 10 #change number of cluster here
k <- 8 # best number of clusters according to CH index
metaClustering <- (metaClustering_consensus(fs$map$codes,k = k,seed=42)) # flowSOM clustering?
seu <- CreateSeuratObject(tm) # create a seurat object
# check the seurat object by plotting some features
print(colnames(df2))
allAB <- colnames(df2)
VlnPlot(seu,features = c("CD56","AQP4")) # eerror in plot
DotPlot(seu, features = c("CD56","AQP4","CD24","GLAST","CD140a","CD29","CD184","CD71","O4","HepaCAM","CD133"))
# scale the data
seu <- ScaleData(seu)
DotPlot(seu, features = c("CD56","AQP4","CD24","GLAST","CD140a","CD29","CD184","CD71","O4","HepaCAM","CD133"))
DotPlot(seu, features = allAB)
#do i need to normalize?
# seu <- NormalizeData(seu, normalization.method = "LogNormalize", scale.factor = 10000)
# create a UMAP
# to do so PCA must be run first
seu <- FindVariableFeatures(seu) # needed for PCA or select all features
#dimentionality reduction methods: pca and umap
seu <-RunPCA(seu,seed.use = 42) # will use variable features by default
seu <- RunPCA(seu, features = allAB)
print(seu[["pca"]], dims = 1:5, nfeatures = 5) # tells you what AB are contributing to the
seu <- RunUMAP(seu,features = allAB)
# Add the clustering data to the seurat object
seu <- AddMetaData(object=seu, metadata=metaClustering[fs$map$mapping[,1]], col.name = 'flowSOM.k.8')
DimPlot(seu, group.by = "flowSOM", reduction = "pca")
DimPlot(seu, group.by = "flowSOM", reduction = "umap") # from normal PCA used as umap input it is mostly on blob, I get the same shape when using all AB features as PCA input
DotPlot (seu, features = allAB, group.by = "flowSOM")
#allow for color labeling
Idents(seu) <- "flowSOM"
FLowSOM clustering visually appear to be terrible.
I’ll just try the seurat clustering
# cluster using Louvain clustering
# using the square root of the number of cells for K might work better
#sqrt(73578) = 271.25
seu <- FindNeighbors(seu, dims = 1:10, k = 271)
seu <- FindClusters(seu, resolution = c(0.1,0.25,0.5,0.75,1))
clustree(seu, prefix = "RNA_snn_res.") + theme(legend.position = "bottom")
# this is very slow - do not run again in notebook
library(clustree)
clustree(seu, prefix = "RNA_snn_res.") + theme(legend.position = "bottom")

# from Clustree 0.5 looks like the best resolution
# c(0.1,0.25,0.5,0.75,1)
resolutions = c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
for (res in resolutions){
print(DimPlot(seu, reduction = "umap", repel = TRUE, label = TRUE, group.by = res))
}





NA
NA
The clusters are not well separated, but better than with flowSOM (visually) Lets see how a dotplot looks
DotPlot(seu, group.by = "RNA_snn_res.0.5", features = allAB, scale = TRUE) # why is the intensity ploted?
# the data must not be working correctly
# maybe the object is not really correct at all
FeatureScatter(seu, feature1 = "CD56",feature2 = "CD24")
FeatureScatter(seu, feature1 = "CD29",feature2 = "CD184")
# maybe the data needs to be normalized
seu <- NormalizeData(seu)
DoHeatmap(seu, features = allAB, group.by = "RNA_snn_res.0.5")
# the groups look okay by heatmap. I'll try the different resolutions
# c(0.1,0.25,0.5,0.75,1)
resolutions = c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
for (res in resolutions){
print(DoHeatmap(seu, features = allAB, group.by = res))
}





# try the dotplot again
# c(0.1,0.25,0.5,0.75,1)
resolutions = c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
for (res in resolutions){
print(DotPlot(seu, features = allAB, group.by = res, cols = c("blue","red")))
}
# what is happening?
Try feature maps
for (AB in allAB){
print(FeaturePlot(seu, features = AB),slot = 'scale.data',min.cutoff = 'q10', max.cutoff ='90')
}













# autothreshold isn't great - I set quartiles
#FeaturePlot(seu, features = "O4", min.cutoff = 1, max.cutoff = 25)
#FeaturePlot(seu, features = "O4", slot = 'scale.data',min.cutoff = 0.1, max.cutoff = 25)
#FeaturePlot(seu, features = "O4", slot = 'scale.data',min.cutoff = 'q5', max.cutoff ='99')
Try ridge plots
RidgePlot(seu, features = c("CD56","CD24"), ncol = 2) # missing values???
VlnPlot(seu, features = c("CD56","CD24"), ncol = 2) # also missing values???
I’ll save the seurat object and see about trying to cluster with phenograph. I believe I have read in the unalligned not normalized data
saveRDS(seu, "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/9MBO/prepro_outsjan20-9000cells/SeuratfromFlowsom.rds")
Phenograph clustering uses Jaccard coefficient between neruest neigbors sets and then id communities by louvain
# install
if(!require(devtools)){
install.packages("devtools") # If not already installed
}
devtools::install_github("JinmiaoChenLab/Rphenograph")
library(Rphenograph)
LS0tCnRpdGxlOiAic3RhdHM6IHNpbGhvdWV0dGUgc2NvcmUsIGNoLCBkYmkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClBhcnQgMTogc3RhdHMKYGBge3J9CgojaW5zdGFsbCByZXF1aXJlZCBwYWNrYWdlCmlmICghcmVxdWlyZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCgpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiRmxvd1NPTSIpICNmb3IgZmxvd3NvbSBjbHVzdGVyaW5nCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJmbG93Q29yZSIpICNmb3IgZmxvd3NvbSBjbHVzdGVyaW5nCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJjbHVzdGVyIikgI2ZvciBzaWxob3VldHRlIHNjb3JlCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJmcGMiKSAjZm9yIGNoIGluZGV4CkJpb2NNYW5hZ2VyOjppbnN0YWxsKCdjbHYnKSAjZm9yIGRiaQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgncGFyRGlzdCcpICMgZm9yIG1ha2luZyBkaXN0cmFuY2UgbWF0cml4IGlucHV0IGZvciBzaWxvdWV0dGUKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCdTZXVyYXQnKSAjZm9yIHBjYSBhbmQgZmxvd3NvbSB2aXN1YWxpemF0aW9uCgppbnN0YWxsLnBhY2thZ2VzKCJwYXJhbGxlbERpc3QiKSAjIGRvZXNuJ3QgaW1wcm92ZSBtZW1vcnkgb24gbXkgbWFjIG1heWJlIG9uIGdydW1pbwppbnN0YWxsLnBhY2thZ2VzKCJ3b3Jkc3BhY2UiKSAjIGNvbnRhaW5zIGRpc3QubWF0cml4IGZ1bmN0aW9uCgpgYGAKCmBgYHtyfQpsaWJyYXJ5KEZsb3dTT00pCmxpYnJhcnkoZmxvd0NvcmUpCmxpYnJhcnkoY2x1c3RlcikKbGlicmFyeShmcGMpCmxpYnJhcnkoY2x2KQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikKCmxpYnJhcnkoInBhcmFsbGVsRGlzdCIpCmxpYnJhcnkoIndvcmRzcGFjZSIpCgpybShsaXN0PWxzKCkpCgpgYGAKCgpgYGB7cn0KCiNpbnB1dCBmaWxlIHBhdGgsIGNoYW5nZSBpZiBuZWVkZWQKZmlsZU5hbWUgPC0iL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzLzlNQk8vcHJlcHJvX291dHNqYW4yMC05MDAwY2VsbHMvcHJlcHJvX291dHNmbG93c2V0LmNzdiIKCiMgbm90ZTogY3VycmVudCBtYXRyaXggc2FtcGxlIElEIGhhdmUgY2VsbCBpbmRleCAjIGF0dGFjaGVkLiAKCmRmIDwtIHJlYWQuY3N2KGZpbGVOYW1lKQpoZWFkKGRmKQpwcmludChkaW0oZGYpKSAjIHRoaXMgaXMgc3BlY2lmaWMgZGYgaGFzIDczNTc4IGNlbGxzCiMgdGhlIHByZXByb2Nlc3Npbmcgb3V0cHV0IGNzdiBuZWVkcyB0byBiZSBjbGVhbmVkIC0gaXQgY29udGFpbnMgbGl2ZSBkZWFkLCBGU0MsIFNTQyBhbmQgdGhlIHNhbXBsZSBjb2x1bW4KZGYyIDwtIGRmICU+JSBzZWxlY3QoLWMoIkxpdmUuRGVhZCIsRlNDLFNTQyxYLEJhdGNoLGNlbGwpKQoKbSA8LSBhcy5tYXRyaXgoZGYyKSAjIGZsb3dzZXQgdGFrZXMgaW5hIG1hdHJpeCBub3QgZGYgCiNtIDwtIG9tWzE6MzAwMCxdICNzdWJzZXQgKG49MzAwMCksIG9taXQgdGhpcyB0byB0ZXN0IHRoZSB3aG9sZSBmaWxlICAtIEkgZGlkbid0IHN1YnNldCBoZXJlIGJ1dCB0b28gOTAwMCBvciBtYXggY2VsbHMgZnJvbSBlYWNoIGZpbGUgYmVmb3JlCgojIGNyZWF0ZSBkaXN0YW5jZSBtYXRyaXgKZG0gPC0gZGlzdC5tYXRyaXgobSwgYXMuZGlzdCA9IFRSVUUpCgojIGlkZWFzIHRvIHNvbHZlIGRpc3QgbWF0cml4IHByb2JsZW06CiMgMSBjYWxjdWxhdGUgaW4gcHl0aG9uIGFuZCBzYXZlIGNzdiB0aGVuIHJlYWQgYXMuZGlzdAojIDIgYWZ0ZXIgY2x1c3RlcmluZyBnZXQgb3V0IHRoZSBleHByZXNzaW9uIG1hdHJpeCBhbmQgdGhlIGNsdXN0ZXIgaWRlYXMgbWFrZSBhIGRmIHN1YnNldCByb3dzICgyNTAwKSwgc2VwYXJhdGUgaW50byBleHByZXNzaW9uIG1hdHJpeCBhbmQgY2x1c3RlciBJRCB2ZWN0b3JzLCBjYWxjdWxhdGUgZGlzdCBtYXRyaXggYW5kIGlucHV0IGludG8gdGhlIHNpbG91ZXR0ZSBmdW5jdGlvbi4gCiMgMy4gZW1haWwgU2FpZWQgbWF5YmUgaGUga25vd3MgYSBiZXR0ZXIKCiN0cnkgc2NhbGluZwojIFNPTSA9IHNlbGYgb3JnYW5pemluZyBtYXAsIE1TVCA9IG1pbmltYWwgc3Bhbm5pbmcgdHJlZQoKIyBpZiByZWFkaW5nIGluIGEgY3N2IGNvbnZlcnQgdG8gZmxvd3NldApmcmFtZSA8LSBuZXcoImZsb3dGcmFtZSIsIGV4cHJzID0gbSkgI2NvbnZlcnQgaW5wdXQgdG8gZmxvd2ZyYW1lCmZzIDwtIFJlYWRJbnB1dChmcmFtZSkgI2NvbnZlcnQgZmxvd2ZyYW1lIHRvIGZsb3dzb20gb2JqZWN0CiMgZnMgPC0gQnVpbGRTT00oZnMsY29sc1RvVXNlPSgtMSkpICMtMSBiZWNhdXNlIHdlIGFyZSBub3QgdXNpbmcgIlgiIGNvbHVtbiB0byBidWlsZCBTT00KZnMgPC0gQnVpbGRTT00oZnMpICMgYnVpbGQgZmxvd1NPTSBvYmplY3QsIG5vIG5lZWQgZm9yIC0xIGJlY2F1c2UgSSBjbGVhbmVkIHRoZSBkZiBhYm91dCBiZWZvcmUgbWFraW5nIGZsb3dzZXQgCmZzIDwtIEJ1aWxkTVNUKGZzKSAjIGJ1aWxkIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSAKIyBCdWlsZE1TVChmbG93U09NIG9iamVjdCBnZW5lcmF0ZWQgYnkgYnVpbGRTT00pCgojIHNhdmUgdGhlIEZsb3dTT00gTVNUIG9iamVjdCB0byB0cnkgYW5kIGF2b2lkIG1lbW9yeSBlcnJvcgojc2F2ZVJEUyhmcywiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzLzlNQk8vcHJlcHJvX291dHNqYW4yMC05MDAwY2VsbHMvRmxvd1NPTWluLnJkcyIpCgoKYGBgCgpTYXZlZCB0aGUgZmxvd3NldCBvYmplY3QgSSBjcmVhdGVkIGFuZCBsb2FkIHRoZSBvYmplY3QgaGVyZSAtIGJ1dCBJIGRpZG4ndCBzYXZlIHRoZSBmaWx0ZXJlZCBkZiBjb252ZXJ0ZWQgdG8gbWF0cml4LCB3aGljaCBpcyBuZWVkZWQgZm9yIHRoZSBzdGF0cyBiZWxvdwoKYGBge3J9CmZzIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy85TUJPL3ByZXByb19vdXRzamFuMjAtOTAwMGNlbGxzL0Zsb3dTT01pbi5yZHMiKQoKIyBub3RlIHRvIFNodW1pbmcgLSB3ZSBzaG91bGQgYmUgYWJsZSB0byBnbyBkaXJlY3RseSBmcm9tIGZsb3dzZXQgaW4gcHJlcHJvY2Vzc2luZyBpbnRvIHRoaXMgZmxvd3NldCBidXQgdGhlbiB0aGUgOSBzYW1wbGVzIChvciBob3dldmVyIG1hbnkgd2lsbCBiZSBpbiBzZXBhcmF0ZSBmcmFtZXMpCiMgSSBkb24ndCBrbm93IHdoZXJlIHRoZSBhY3R1YWwgdmFsdWVzIGFyZSBsb2NhdGVkCgpgYGAKCgoKYGBge3J9CiMgc2tpcAojYmFzZWQgb24gZmxvd3NvbSwgdGhlIG9wdGltYWwgayBudW1iZXIgb2YgY2x1c3RlcnMgPSA1IGZvciB0aGUgc2FtcGxlIChuPTMwMDApIAptZXRhY2wgPC0gTWV0YUNsdXN0ZXJpbmcoZnMkbWFwJGNvZGVzLAoibWV0YUNsdXN0ZXJpbmdfY29uc2Vuc3VzIiwKbWF4ID0gMTUpCgp1bmlxdWUobWV0YWNsW2ZzJG1hcCRtYXBwaW5nWywgMV1dKQpgYGAKCgoKCgpgYGB7cn0KCiNhIGZ1bmN0aW9uIHRoYXQgY2FsY3VsYXRlIDMgc3RhdHMgZm9yIGsgbnVtYmVyIG9mIGNsdXN0ZXJpbmcKIyB0aGUgc2lsaG91ZXR0ZSBzdGF0IHRha2VzIHRvbyBtdWNoIG1lbW9yeSBhbmQgd2Ugd2lsbCBub3QgcnVuIHRoaXMgZm9yIG5vdy4KCiMgU2h1bWluZyBJIG5lZWQgdGhpcyBmdW5jdGlvbiB0byB0YWtlIGluIGEgbGlzdCBvZiBjbHVzdGVyaW5nIGluZGV4ZXMgYW5kIGFuIGV4cHJlc3Npb24gbWF0cml4CgpzdGF0cyA8LSBmdW5jdGlvbihrcmFuZ2UpewogIyBzaV9saSA8LWxpc3QoKQogIGNoX2xpIDwtbGlzdCgpCiAgZGJpX2xpIDwtbGlzdCgpCiAgCiAgayA9IDMKICBkbSA9IHBhckRpc3QobSkKICBkbSA9IGRpc3QobSwgbWV0aG9kID0gIm1pbmtvd3NraSIpCiAgZG0gPSBkaXN0Lm1hdHJpeChtLCBhcy5kaXN0ID0gVFJVRSkKICAKICBmb3IgKGsgaW4ga3JhbmdlKSB7CiAgICAKICAgICNmbG93c29tIGNsdXN0ZXJpbmcsIHRyeSBlYWNoIGsgaW4ga3JhbmdlCiAgICBtZXRhQ2x1c3RlcmluZyA8LSAobWV0YUNsdXN0ZXJpbmdfY29uc2Vuc3VzKGZzJG1hcCRjb2RlcyxrID0gayxzZWVkPTQyKSkgIyBoZXJlIHdlIGFyZSBjbHVzdGVyaW5nIHdpdGhpbiB0aGUgbG9vcAogICAgIyBAU2h1bWluZyB3ZSBuZWVkIHRvIGNoYW5nZSB0aGlzIHNvIHRoYXQgdGhlIGxvb3AgY2FuIHB1bGwgdGhlIHJlc3VsdHMgb2YgY2x1c3RlcmluZyBvdXQgb2YgYW5vdGhlciBkYXRhIG9iamVjdAogICAgIyBob3cgYW0gSSBnb2luZyB0byB0ZXN0IHRoZSBTZXVyYXQgb3IgUGhlbm9ncmFwaCBjbHVzdGVyaW5nPwoKICAgICNjYWxjdWxhdGUgc2lsaG91ZXR0ZSBzY29yZQogICAgIyBjbHVzdGVyIGluZGV4LCBkaXN0YW5jZSBtYXRyaXggb2YgcGFpcnMgLS0tICBtYXliZSBjYWxjdWxhdGUgaW4gYWR2YW5jZSAKICAgIHNpIDwtIHNpbGhvdWV0dGUobWV0YUNsdXN0ZXJpbmdbZnMkbWFwJG1hcHBpbmdbLCAxXV0sZGlzdChtKSwpCgogICAgc2lfbGlba10gPC0gbWVhbihzaVssIDNdKQogICAgCiAgICAjY2FsY3VsYXRlIENhbGluc2tpLUhhcmFiYXN6IGluZGV4CiAgICAjIGNhbGluaGFyYSh4LGNsdXN0ZXJpbmcsY249bWF4KGNsdXN0ZXJpbmcpKQogICAgIyB3aGVyZSB4IGlzIGEgbWF0cml4IG9yIGRhdGFmcmFtZQogICAgY2ggPSBjYWxpbmhhcmEobSxtZXRhQ2x1c3RlcmluZ1tmcyRtYXAkbWFwcGluZ1ssIDFdXSxjbj1tYXgobWV0YUNsdXN0ZXJpbmdbZnMkbWFwJG1hcHBpbmdbLDFdXSkpCiAgICAKICAgIGNoX2xpW2tdIDwtIGNoCiAgICAKICAgICNjYWxjdWxhdGUgRGF2aWVz4oCTQm91bGRpbiBpbmRleAogICAgZGJpID0gY2x2LkRhdmllcy5Cb3VsZGluKGNscy5zY2F0dC5kYXRhKAogICAgICBtLAogICAgICBtZXRhQ2x1c3RlcmluZ1tmcyRtYXAkbWFwcGluZ1ssIDFdXSksCiAgICAgIGludHJhY2xzID0gImF2ZXJhZ2UiLAogICAgICBpbnRlcmNscyA9ICJhdmVyYWdlIgogICAgKQoKICAgIGRiaV9saVtrXSA8LSBkYmlbMV0KICAgIH0gCgogIHJldHVybihsaXN0KGNoX2xpLCBkYmlfbGkpKQp9CgoKCmBgYAoKCmBgYHtyfQojIHJ1biB0aGUgZnVuY3Rpb24gdG8gZ2V0IHRoZSBzdGF0cwoKCgprcmFuZ2UgPC0gMzoxNSAjcmFuZ2Ugb2YgbnVtYmVyIG9mIGNsdXN0ZXJzCmxpID0gc3RhdHMoa3JhbmdlKSAgIyBkb2Vzbid0IGNvbnRhaW4gc2lsaG91ZXR0ZSAKIyBsaXN0IG9mIDIgCgojc2lsaG91ZXR0ZSBzY29yZTogcmFuZ2VzIGZyb20gLTEgIHRvIDEgCiMtMTogYmFkIGNsdXN0ZXJzICAwOiBuZXV0cmFsLCBpbmRpZmZlcmVudCAgMTogZ29vZCBjbHVzdGVycwojcGxvdChrcmFuZ2UsIHR5cGU9J2InLCBsaVtbMV1dW2tyYW5nZV0sIHhsYWI9J051bWJlciBvZiBjbHVzdGVycycsIHlsYWI9J0F2ZXJhZ2UgU2lsaG91ZXR0ZSBTY29yZXMnLCBmcmFtZT1UUlVFKQoKI0NhbGluc2tpLUhhcmFiYXN6IGluZGV4OiAKIyB0aGUgaGlnaGVzdCB2YWx1ZSBpcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMKcGxvdChrcmFuZ2UsIHR5cGU9J2InLCBsaVtbMV1dW2tyYW5nZV0sIHhsYWI9J051bWJlciBvZiBjbHVzdGVycycsIHlsYWI9J0NhbGluc2tpLUhhcmFiYXN6IGluZGV4JywgZnJhbWU9VFJVRSkKCiNEYXZpZXPigJNCb3VsZGluIGluZGV4OiBtaW5pbXVtIHNjb3JlIGlzIHplcm8KI3RoZSBsb3dlc3QgdmFsdWUgaXMgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzCnBsb3Qoa3JhbmdlLCB0eXBlPSdiJywgbGlbWzJdXVtrcmFuZ2VdLCB4bGFiPSdOdW1iZXIgb2YgY2x1c3RlcnMnLCB5bGFiPSdEYXZpZXPigJNCb3VsZGluIGluZGV4JywgZnJhbWU9VFJVRSkKCgpgYGAKVGhlIENhbGluc2tpLUhhcmFiYXN6IGluZGV4IHNob3cgdGhhdCBLPTggaXMgYmVzdCBudW1iZXIgb2YgY2x1c3RlcnMuICBCdXQgdGhlIERhdmllc+KAk0JvdWxkaW4gaW5kZXggc2hvd3MgbG93ZXIgaXMgYmV0dGVyLiAgSXQgaXMgb2theSB1cCB1bnRpbCA5LiAKCgpUcnkgc3RhdGlzdGljcyBvbiBvdGhlciBjbHVzdGVyaW5nLiAgCgoKYGBge3J9CiMgZnJvbSBQaGVub2dyYXBoIAojIHJlYWQgaW4gdGhlIGRmIHdpdGggcGhlbm9ncmFwaCBjbHVzdGVycyBhbmQgc2F2ZWQgZXhwcmVzc2lvbiBtYXRyaXggb3V0cHV0IGZyb20gcHJlcHJvY2Vzc2luZyAtIG5vIGFsaWduZW1lbnQgb3Igbm9ybWFsaXphdGlvbgpwcmludChjb2xuYW1lcyhkZikpCmFsbEFCIDwtIGMoIkFRUDQiLCJDRDU2IiwiR0xBU1QiLCAiQ0QxNDBhIiwiQ0QyOSIsICJDRDQ0IiwgIkNEMTg0IiwiQ0Q3MSIsIkNEMjQiLCJDRDE1IiwiTzQiLCJIZXBhQ0FNIiwiQ0QxMzMiKQojIHdlIG5lZWQganVzdCB0aGUgZXhwcmVzc2lvbiBtYXRyaXggCmRmMiA8LSBkZiAlPiUgc2VsZWN0KGMoIkFRUDQiLCJDRDU2IiwiR0xBU1QiLCAiQ0QxNDBhIiwiQ0QyOSIsICJDRDQ0IiwgIkNEMTg0IiwiQ0Q3MSIsIkNEMjQiLCJDRDE1IiwiTzQiLCJIZXBhQ0FNIiwiQ0QxMzMiKSkKCm0gPC0gYXMubWF0cml4KGRmMikKCgojY2FsY3VsYXRlIENhbGluc2tpLUhhcmFiYXN6IGluZGV4CiAgICAjIGNhbGluaGFyYSh4LGNsdXN0ZXJpbmcsY249bWF4KGNsdXN0ZXJpbmcpKQogICAgIyB3aGVyZSB4IGlzIGEgbWF0cml4IG9yIGRhdGFmcmFtZQpjaC5waGVuby5rLjI3MSA9IGNhbGluaGFyYShtLGRmJHBoZW5vZ3JhcGhfY2x1c3RlcmsyNzEsbGVuZ3RoKCh1bmlxdWUoZGYkcGhlbm9ncmFwaF9jbHVzdGVyazI3MSkpKSkKCmNoLnBoZW5vLmsuNTAgPSBjYWxpbmhhcmEobSxkZiRwaGVub2dyYXBoX2NsdXN0ZXIsbGVuZ3RoKCh1bmlxdWUoZGYkcGhlbm9ncmFwaF9jbHVzdGVyKSkpKSAgIAoKIyB0aGlzIHdvcmtzIHdlbGwgdG8gc2VlIHRoZSBzdGF0aXN0aWNzIGJ1dCBub3QgZ3JlYXQgZm9yIGEgcGxvdC4KIyBJIG1pZ2h0IG5lZWQgdG8ganVzdCBtYWtlIGEgdGFiZWwKCgoKCmBgYAoKRGF2aWQgQm91bGRpbiBpbmRleCBmb3IgUGhlbm9ncmFwaCAKCmBgYHtyfQojY2FsY3VsYXRlIERhdmllc+KAk0JvdWxkaW4gaW5kZXgKIyBpbmRpZXgubGlzdCwgaW50cmFjbHMsIGludGVyY2xzIAojIEknbSBub3Qgc3VyZSBob3cgdGhpcyBpcyB3b3JraW5nIGJ1dCBpdCBkb2VzIHJ1bgpkYmkucGhlbi5rLjI3MSA8LSBjbHYuRGF2aWVzLkJvdWxkaW4oY2xzLnNjYXR0LmRhdGEoCiAgICAgIG0sCiAgICAgIGRmJHBoZW5vZ3JhcGhfY2x1c3RlcmsyNzEpLAogICAgICBpbnRyYWNscyA9ICJhdmVyYWdlIiwKICAgICAgaW50ZXJjbHMgPSAiYXZlcmFnZSIKICAgICkKICAKZGJpLnBoZW4uay41MCA8LSBjbHYuRGF2aWVzLkJvdWxkaW4oY2xzLnNjYXR0LmRhdGEoCiAgICAgIG0sCiAgICAgIGRmJHBoZW5vZ3JhcGhfY2x1c3RlciksCiAgICAgIGludHJhY2xzID0gImF2ZXJhZ2UiLAogICAgICBpbnRlcmNscyA9ICJhdmVyYWdlIgogICAgKQoKCmBgYAoKCm1ha2UgdGhpcyBjYWxjdWxhdGlvbnMgb24gdGhlIFNldXJhdCBjbHVzdGVyaW5nCgoKYGBge3J9CiMgcmVhZCBpbiB0aGUgc2V1cmF0IG9iamVjdCB3aXRoIHRoZSBjbHVzdGVyaW5nIHZhbHVlcwpzZXUgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzLzlNQk8vcHJlcHJvX291dHNqYW4yMC05MDAwY2VsbHMvU2V1cmF0ZnJvbUZsb3dzb21QaGVuby5yZHMiICkKCgpgYGAKCkNIIGluZGV4IGFuZCBEQkkgZnJvbSBTZXVyYXQgKExvdXZhaW4pIHRyeSBvbmUgcmVzb2x1dGlvbiBmaXJzdAoKYGBge3J9CgpjaC5sb3V2LnJlcy4xID0gY2FsaW5oYXJhKG0sc2V1QG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4xLGxlbmd0aCgodW5pcXVlKHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMSkpKSkKIyB0aGlzIHdvcmtzCgpkYmkubG91di5yZXMuMSA8LSBjbHYuRGF2aWVzLkJvdWxkaW4oY2xzLnNjYXR0LmRhdGEoCiAgICAgIG0sCiAgICAgIHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMSksCiAgICAgIGludHJhY2xzID0gImF2ZXJhZ2UiLAogICAgICBpbnRlcmNscyA9ICJhdmVyYWdlIgogICAgKQojIHRoaXMgZG9lc24ndCB3b3JrIC0gbm90IHN1cmUgd2hlcmUgdG8gc3BlY2lmeSBob3cgdG8gZmluZCB0aGUgZGF0YSB0byBjYWxjdWxhdGUgaW50cmFjbHMgYW5kIGludGVyY2xzCgoKYGBgCgpDYWxjdWxhdGUgYWxsIENISSBmb3IgTG91dmFpbgoKYGBge3J9CgogIGNoX2xvdXYgPC1saXN0KCkKbC5yYW5nZSA8LSBjKCJSTkFfc25uX3Jlcy4wLjEiLCJSTkFfc25uX3Jlcy4wLjI1IiwiUk5BX3Nubl9yZXMuMC41IiwiUk5BX3Nubl9yZXMuMC43NSIsIlJOQV9zbm5fcmVzLjEiKQpyZXNvbHV0aW9uID0gYygwLjEsMC4yNSwwLjUsMC43NSwxKQpwcmVmaXggPSAiUk5BX3Nubl9yZXMuIiAKIyBsb29wIGRvZXNuJ3Qgd29yayBiZWNhdXNlIEkgY2FuJ3QgaW5kZXggdGhlIHNldXJhdCByZXNvbHV0aW9ucwoKY2ggPSBjYWxpbmhhcmEobSxzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuMSxsZW5ndGgoKHVuaXF1ZShzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuMSkpKSkKY2hfbG91dlsiUk5BX3Nubl9yZXMuMC4xIl0gPC0gY2gKCmNoID0gY2FsaW5oYXJhKG0sc2V1QG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4wLjI1LGxlbmd0aCgodW5pcXVlKHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMC4yNSkpKSkKY2hfbG91dlsiUk5BX3Nubl9yZXMuMC4yNSJdIDwtIGNoCgpjaCA9IGNhbGluaGFyYShtLHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMC41LGxlbmd0aCgodW5pcXVlKHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMC41KSkpKQpjaF9sb3V2WyJSTkFfc25uX3Jlcy4wLjUiXSA8LSBjaAoKY2ggPSBjYWxpbmhhcmEobSxzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuNzUsbGVuZ3RoKCh1bmlxdWUoc2V1QG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4wLjc1KSkpKQpjaF9sb3V2WyJSTkFfc25uX3Jlcy4wLjc1Il0gPC0gY2gKCmNoID0gY2FsaW5oYXJhKG0sc2V1QG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4xLGxlbmd0aCgodW5pcXVlKHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMSkpKSkKY2hfbG91dlsiUk5BX3Nubl9yZXMuMSJdIDwtIGNoCgoKCgpgYGAKClBsb3QgdGhlIENISSBmcm9tIExvdXZhaW4gY2x1c3RlcnMKYGBge3J9CgojIHRoZSBoaWdoZXN0IHZhbHVlIGlzIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycwp5IDwtIHNhcHBseShjaF9sb3V2LCAnWycsICkKcGxvdChyZXNvbHV0aW9uLCB5LCB4bGFiPSdSZXNvbHV0aW9uIG9mIGNsdXN0ZXJzJyx0eXBlID0gImIiLCB5bGFiPSdDYWxpbnNraS1IYXJhYmFzeiBpbmRleCcsIGZyYW1lPVRSVUUpCgoKCgpgYGAKCk1ha2UgYSB0YWJsZSB3aXRoIHRoZSBDSEkgb3V0cHV0cwoKYGBge3J9CgpDbHVzdGVyLnR5cGUgPC0gYygiUGhlbm9ncmFwaC5rLjUwIiwiUGhlbm9ncmFwaC5rLjI3MSIsbC5yYW5nZSkKcHJpbnQoQ2x1c3Rlci50eXBlKQpNZXRob2QgPC0gYygiUGhlbm9ncmFwaC5rLjUwIiAsICJQaGVub2dyYXBoLmsuMjcxIiAsIlJOQV9zbm5fcmVzLjAuMSIsICAiUk5BX3Nubl9yZXMuMC4yNSIgLCJSTkFfc25uX3Jlcy4wLjUiICwgIlJOQV9zbm5fcmVzLjAuNzUiLCAiUk5BX3Nubl9yZXMuMSIpCgpwcmludCh5KQoKQ0hJLnZhbHVlIDwtIGMoNzQxOC4yOTksNzgwNi4xMzAsIDUyNTIxLjEyMCwgIDk5MzAuMjM1LCAgNzczNC4wNjUsICA0OTQ5LjU5MywgIDQyMjkuMjUyKQoKCgpkZi5jaGkgPC0gY2JpbmQuZGF0YS5mcmFtZShNZXRob2QsIENISS52YWx1ZSkKCgoKCmBgYAoKTWFrZSBhbm90aGVyIHBsb3QKCmBgYHtyfQojbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGYuY2hpLCBhZXMoeD0gTWV0aG9kLCB5PSBDSEkudmFsdWUpKSArIGdlb21fcG9pbnQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCgojIGhvdyBkbyB0aGVzZSB2YWx1ZXMgY29tcGFyZSB3aXRoIAoKYGBgCgoKCgpQYXJ0IDI6IHBsb3QKCmBgYHtyfQoKI3RyYW5zcG9zZSB0aGUgY3N2IHNvIHRoYXQgc2V1cmF0IG9iamVjdCBoYXMgdGhlIHJpZ2h0IGNvbHVtbiBhbmQgcm93CiMgdGhlIGZsb3cgaW50ZW5zaXR5IHZhbHVlcyB3aWxsIGJlIGlucHV0IGFzIFJOQSBleHByZXNzaW9uCnRtIDwtIHQoZGYyKQpyb3duYW1lcyh0bSkgPC0gY29sbmFtZXMoZGYyKQpjb2xuYW1lcyh0bSkgPC0gcm93bmFtZXMoZGYyKQoKCmsgPC0gMTAgI2NoYW5nZSBudW1iZXIgb2YgY2x1c3RlciBoZXJlIAprIDwtIDggIyBiZXN0IG51bWJlciBvZiBjbHVzdGVycyBhY2NvcmRpbmcgdG8gQ0ggaW5kZXgKCm1ldGFDbHVzdGVyaW5nIDwtIChtZXRhQ2x1c3RlcmluZ19jb25zZW5zdXMoZnMkbWFwJGNvZGVzLGsgPSBrLHNlZWQ9NDIpKSAjIGZsb3dTT00gY2x1c3RlcmluZz8KCnNldSA8LSBDcmVhdGVTZXVyYXRPYmplY3QodG0pICMgY3JlYXRlIGEgc2V1cmF0IG9iamVjdCAKIyBjaGVjayB0aGUgc2V1cmF0IG9iamVjdCBieSBwbG90dGluZyBzb21lIGZlYXR1cmVzCnByaW50KGNvbG5hbWVzKGRmMikpCmFsbEFCIDwtIGNvbG5hbWVzKGRmMikKVmxuUGxvdChzZXUsZmVhdHVyZXMgPSBjKCJDRDU2IiwiQVFQNCIpKSAjIGVlcnJvciBpbiBwbG90CkRvdFBsb3Qoc2V1LCBmZWF0dXJlcyA9IGMoIkNENTYiLCJBUVA0IiwiQ0QyNCIsIkdMQVNUIiwiQ0QxNDBhIiwiQ0QyOSIsIkNEMTg0IiwiQ0Q3MSIsIk80IiwiSGVwYUNBTSIsIkNEMTMzIikpCiMgc2NhbGUgdGhlIGRhdGEgCnNldSA8LSBTY2FsZURhdGEoc2V1KQoKRG90UGxvdChzZXUsIGZlYXR1cmVzID0gYygiQ0Q1NiIsIkFRUDQiLCJDRDI0IiwiR0xBU1QiLCJDRDE0MGEiLCJDRDI5IiwiQ0QxODQiLCJDRDcxIiwiTzQiLCJIZXBhQ0FNIiwiQ0QxMzMiKSkKRG90UGxvdChzZXUsIGZlYXR1cmVzID0gYWxsQUIpCiNkbyBpIG5lZWQgdG8gbm9ybWFsaXplPwojIHNldSA8LSBOb3JtYWxpemVEYXRhKHNldSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCgojIGNyZWF0ZSBhIFVNQVAKIyB0byBkbyBzbyBQQ0EgbXVzdCBiZSBydW4gZmlyc3QgCnNldSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzZXUpICMgbmVlZGVkIGZvciBQQ0Egb3Igc2VsZWN0IGFsbCBmZWF0dXJlcwojZGltZW50aW9uYWxpdHkgcmVkdWN0aW9uIG1ldGhvZHM6IHBjYSBhbmQgdW1hcAoKc2V1IDwtUnVuUENBKHNldSxzZWVkLnVzZSA9IDQyKSAjIHdpbGwgdXNlIHZhcmlhYmxlIGZlYXR1cmVzIGJ5IGRlZmF1bHQKc2V1IDwtIFJ1blBDQShzZXUsIGZlYXR1cmVzID0gYWxsQUIpCgpwcmludChzZXVbWyJwY2EiXV0sIGRpbXMgPSAxOjUsIG5mZWF0dXJlcyA9IDUpICMgdGVsbHMgeW91IHdoYXQgQUIgYXJlIGNvbnRyaWJ1dGluZyB0byB0aGUgCnNldSA8LSBSdW5VTUFQKHNldSxmZWF0dXJlcyA9IGFsbEFCKQoKIyBBZGQgdGhlIGNsdXN0ZXJpbmcgZGF0YSB0byB0aGUgc2V1cmF0IG9iamVjdApzZXUgPC0gQWRkTWV0YURhdGEob2JqZWN0PXNldSwgbWV0YWRhdGE9bWV0YUNsdXN0ZXJpbmdbZnMkbWFwJG1hcHBpbmdbLDFdXSwgY29sLm5hbWUgPSAnZmxvd1NPTS5rLjgnKQoKRGltUGxvdChzZXUsIGdyb3VwLmJ5ID0gImZsb3dTT00iLCByZWR1Y3Rpb24gPSAicGNhIikgCkRpbVBsb3Qoc2V1LCBncm91cC5ieSA9ICJmbG93U09NIiwgcmVkdWN0aW9uID0gInVtYXAiKSAjIGZyb20gbm9ybWFsIFBDQSB1c2VkIGFzIHVtYXAgaW5wdXQgaXQgaXMgbW9zdGx5IG9uIGJsb2IsIEkgZ2V0IHRoZSBzYW1lIHNoYXBlIHdoZW4gdXNpbmcgYWxsIEFCIGZlYXR1cmVzIGFzIFBDQSBpbnB1dAoKRG90UGxvdCAoc2V1LCBmZWF0dXJlcyA9IGFsbEFCLCBncm91cC5ieSA9ICJmbG93U09NIikKCgojYWxsb3cgZm9yIGNvbG9yIGxhYmVsaW5nCklkZW50cyhzZXUpIDwtICJmbG93U09NIgpgYGAKCkZMb3dTT00gY2x1c3RlcmluZyB2aXN1YWxseSBhcHBlYXIgdG8gYmUgdGVycmlibGUuCgpJJ2xsIGp1c3QgdHJ5IHRoZSBzZXVyYXQgY2x1c3RlcmluZwoKYGBge3J9CgojIGNsdXN0ZXIgdXNpbmcgTG91dmFpbiBjbHVzdGVyaW5nCiMgdXNpbmcgdGhlIHNxdWFyZSByb290IG9mIHRoZSBudW1iZXIgb2YgY2VsbHMgZm9yIEsgbWlnaHQgd29yayBiZXR0ZXIKI3NxcnQoNzM1NzgpID0gMjcxLjI1CnNldSA8LSBGaW5kTmVpZ2hib3JzKHNldSwgZGltcyA9IDE6MTAsIGsgPSAyNzEpCnNldSA8LSBGaW5kQ2x1c3RlcnMoc2V1LCByZXNvbHV0aW9uID0gYygwLjEsMC4yNSwwLjUsMC43NSwxKSkKY2x1c3RyZWUoc2V1LCBwcmVmaXggPSAiUk5BX3Nubl9yZXMuIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKIyB0aGlzIGlzICB2ZXJ5IHNsb3cgLSBkbyBub3QgcnVuIGFnYWluIGluIG5vdGVib29rCgpgYGAKCmBgYHtyfQpsaWJyYXJ5KGNsdXN0cmVlKQpjbHVzdHJlZShzZXUsIHByZWZpeCA9ICJSTkFfc25uX3Jlcy4iKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCmBgYHtyfQojIGZyb20gQ2x1c3RyZWUgMC41IGxvb2tzIGxpa2UgdGhlIGJlc3QgcmVzb2x1dGlvbgoKIyBjKDAuMSwwLjI1LDAuNSwwLjc1LDEpCnJlc29sdXRpb25zID0gYygiUk5BX3Nubl9yZXMuMC4xIiwiUk5BX3Nubl9yZXMuMC4yNSIsIlJOQV9zbm5fcmVzLjAuNSIsIlJOQV9zbm5fcmVzLjAuNzUiLCJSTkFfc25uX3Jlcy4xIikKCmZvciAocmVzIGluIHJlc29sdXRpb25zKXsKIHByaW50KERpbVBsb3Qoc2V1LCByZWR1Y3Rpb24gPSAidW1hcCIsIHJlcGVsID0gVFJVRSwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9IHJlcykpCiAKfQoKCmBgYAoKVGhlIGNsdXN0ZXJzIGFyZSBub3Qgd2VsbCBzZXBhcmF0ZWQsIGJ1dCBiZXR0ZXIgdGhhbiB3aXRoIGZsb3dTT00gKHZpc3VhbGx5KQpMZXRzIHNlZSBob3cgYSBkb3RwbG90IGxvb2tzCgpgYGB7cn0KRG90UGxvdChzZXUsIGdyb3VwLmJ5ID0gIlJOQV9zbm5fcmVzLjAuNSIsIGZlYXR1cmVzID0gYWxsQUIsIHNjYWxlID0gVFJVRSkgIyB3aHkgaXMgdGhlIGludGVuc2l0eSBwbG90ZWQ/CiMgdGhlIGRhdGEgbXVzdCBub3QgYmUgd29ya2luZyBjb3JyZWN0bHkKCgojIG1heWJlIHRoZSBvYmplY3QgaXMgbm90IHJlYWxseSBjb3JyZWN0IGF0IGFsbApGZWF0dXJlU2NhdHRlcihzZXUsIGZlYXR1cmUxID0gIkNENTYiLGZlYXR1cmUyID0gIkNEMjQiKQpGZWF0dXJlU2NhdHRlcihzZXUsIGZlYXR1cmUxID0gIkNEMjkiLGZlYXR1cmUyID0gIkNEMTg0IikKCiMgbWF5YmUgdGhlIGRhdGEgbmVlZHMgdG8gYmUgbm9ybWFsaXplZApzZXUgPC0gTm9ybWFsaXplRGF0YShzZXUpCgpEb0hlYXRtYXAoc2V1LCBmZWF0dXJlcyA9IGFsbEFCLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4wLjUiKQojIHRoZSBncm91cHMgbG9vayBva2F5IGJ5IGhlYXRtYXAuIEknbGwgdHJ5IHRoZSBkaWZmZXJlbnQgcmVzb2x1dGlvbnMgCgoKCmBgYAoKYGBge3J9CiMgYygwLjEsMC4yNSwwLjUsMC43NSwxKQpyZXNvbHV0aW9ucyA9IGMoIlJOQV9zbm5fcmVzLjAuMSIsIlJOQV9zbm5fcmVzLjAuMjUiLCJSTkFfc25uX3Jlcy4wLjUiLCJSTkFfc25uX3Jlcy4wLjc1IiwiUk5BX3Nubl9yZXMuMSIpCgpmb3IgKHJlcyBpbiByZXNvbHV0aW9ucyl7CiBwcmludChEb0hlYXRtYXAoc2V1LCBmZWF0dXJlcyA9IGFsbEFCLCBncm91cC5ieSA9IHJlcykpCiAKfQoKYGBgCgpgYGB7cn0KIyB0cnkgdGhlIGRvdHBsb3QgYWdhaW4KIyBjKDAuMSwwLjI1LDAuNSwwLjc1LDEpCnJlc29sdXRpb25zID0gYygiUk5BX3Nubl9yZXMuMC4xIiwiUk5BX3Nubl9yZXMuMC4yNSIsIlJOQV9zbm5fcmVzLjAuNSIsIlJOQV9zbm5fcmVzLjAuNzUiLCJSTkFfc25uX3Jlcy4xIikKCmZvciAocmVzIGluIHJlc29sdXRpb25zKXsKIHByaW50KERvdFBsb3Qoc2V1LCBmZWF0dXJlcyA9IGFsbEFCLCBncm91cC5ieSA9IHJlcywgY29scyA9IGMoImJsdWUiLCJyZWQiKSkpCiAKfQoKIyB3aGF0IGlzIGhhcHBlbmluZz8KCmBgYAoKVHJ5IGZlYXR1cmUgbWFwcwoKYGBge3J9CgoKZm9yIChBQiBpbiBhbGxBQil7CiBwcmludChGZWF0dXJlUGxvdChzZXUsIGZlYXR1cmVzID0gQUIpLHNsb3QgPSAnc2NhbGUuZGF0YScsbWluLmN1dG9mZiA9ICdxMTAnLCBtYXguY3V0b2ZmID0nOTAnKQogCn0KCiMgYXV0b3RocmVzaG9sZCBpc24ndCBncmVhdCAtIEkgc2V0IHF1YXJ0aWxlcyAKCiNGZWF0dXJlUGxvdChzZXUsIGZlYXR1cmVzID0gIk80IiwgbWluLmN1dG9mZiA9IDEsIG1heC5jdXRvZmYgPSAyNSkKI0ZlYXR1cmVQbG90KHNldSwgZmVhdHVyZXMgPSAiTzQiLCBzbG90ID0gJ3NjYWxlLmRhdGEnLG1pbi5jdXRvZmYgPSAwLjEsIG1heC5jdXRvZmYgPSAyNSkKCiNGZWF0dXJlUGxvdChzZXUsIGZlYXR1cmVzID0gIk80Iiwgc2xvdCA9ICdzY2FsZS5kYXRhJyxtaW4uY3V0b2ZmID0gJ3E1JywgbWF4LmN1dG9mZiA9Jzk5JykKCgoKCmBgYAoKVHJ5IHJpZGdlIHBsb3RzCgpgYGB7cn0KUmlkZ2VQbG90KHNldSwgZmVhdHVyZXMgPSBjKCJDRDU2IiwiQ0QyNCIpLCBuY29sID0gMikgIyBtaXNzaW5nIHZhbHVlcz8/PwpWbG5QbG90KHNldSwgZmVhdHVyZXMgPSBjKCJDRDU2IiwiQ0QyNCIpLCBuY29sID0gMikgIyBhbHNvIG1pc3NpbmcgdmFsdWVzPz8/CgpgYGAKCgpJJ2xsIHNhdmUgdGhlIHNldXJhdCBvYmplY3QgYW5kIHNlZSBhYm91dCB0cnlpbmcgdG8gY2x1c3RlciB3aXRoIHBoZW5vZ3JhcGguICBJIGJlbGlldmUgSSBoYXZlIHJlYWQgaW4gdGhlIHVuYWxsaWduZWQgbm90IG5vcm1hbGl6ZWQgZGF0YSAKCgpgYGB7cn0Kc2F2ZVJEUyhzZXUsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvOU1CTy9wcmVwcm9fb3V0c2phbjIwLTkwMDBjZWxscy9TZXVyYXRmcm9tRmxvd3NvbS5yZHMiKQoKCgpgYGAKCgpQaGVub2dyYXBoIGNsdXN0ZXJpbmcgdXNlcyBKYWNjYXJkIGNvZWZmaWNpZW50IGJldHdlZW4gbmVydWVzdCBuZWlnYm9ycyBzZXRzIGFuZCB0aGVuIGlkIGNvbW11bml0aWVzIGJ5IGxvdXZhaW4gCgoKYGBge3J9CiMgaW5zdGFsbAppZighcmVxdWlyZShkZXZ0b29scykpewogIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikgIyBJZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQKfQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIkppbm1pYW9DaGVuTGFiL1JwaGVub2dyYXBoIikKCmxpYnJhcnkoUnBoZW5vZ3JhcGgpCgpgYGAKCgoKCg==